package lm.spring.feign.support.decoder;

import feign.Request;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import lm.spring.feign.support.exception.ApiJsonException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * Api errorDecoder
 */
public class ApiErrorDecoder implements ErrorDecoder {
    private static final Logger log = LoggerFactory.getLogger(ApiErrorDecoder.class);

    private final ErrorDecoder errorDecoder = new Default();

    /**
     * Decode an HTTP {@link Response}.
     *
     * @param methodKey configKey of the java method that invoked the request.
     * @param response  HTTP response.
     * @return Exception.
     */
    public Exception decode(String methodKey, Response response) {
        if (response.status() >= 400 && response.status() <= 599) {
            log.error("status {} reading {}, url: {}, request body: {}, response: {}"
                    , response.status(), methodKey, response.request().url()
                    , requestBodyAsString(response.request())
                    , response.toString()
            );
        }

        boolean jsonResp = isJsonResp(response);
        if (jsonResp) {
            try {
                String message = Util.toString(response.body().asReader(Util.UTF_8));
                return new ApiJsonException(message);
            } catch (IOException e) {
                log.error("响应信息转换json失败:" + response.toString(), e);
                return errorDecoder.decode(methodKey, response);
            }
        } else {
            return errorDecoder.decode(methodKey, response);
        }
    }

    private boolean isJsonResp(Response response) {
        boolean isJson = false;
        for (String ct : response.headers().get("content-type")) {
            if (ct.contains("json")) {
                isJson = true;
                break;
            }
        }
        return isJson;
    }

    /**
     * Retrieve request body as string.
     * <p>
     * Derived from feign.Request.Body.asString()
     * <p>
     * Since (inclusively) feign 10.7.3, Request.requestBody().asString() is not available, so
     * we need to workaround it.
     *
     * @param request
     * @return
     */
    private static String requestBodyAsString(Request request) {
        return !request.isBinary()
                ? new String(request.body(), request.charset())
                : "Binary data";
    }
}
